#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import logging

import graph

class RepositoryError(Exception):
    def __init__(self, message):
        Exception.__init__(self, message)

class Repository:
    #synchronization needed
    #exception handling needed

    #add singleton logic
    def __init__(self, storage):
        self.__logger = logging.getLogger("Repository")
        self.__graph = graph.digraph()
        self.__storage = storage

        self.__fill_graph()

    @property
    def storage(self):
        return self.__storage

    def find_packets_by_name(self, name):
        list = []

        self.__logger.debug("Find packets by name = %s...", name)
        for each in self.__graph.nodes():
            packet = self.__graph.get_node_attributes(each)
            meta = packet.meta
            if meta.name == name:
                list.append(packet)

        self.__logger.debug("[%s] packets has been found", len(list))

        return list

    def find_packet_by_fullname(self, full_name):
        self.__logger.debug("Find packet by full_name = %s", full_name)
        result = None
        for each in self.__graph.nodes():
            if each == full_name:
                packet = self.__graph.get_node_attributes(each)

                self.__logger.debug("Packet with full_name = %s has been found", full_name)
                result = packet

        return result

    def find_packet_neighbors_by_fullname(self, full_name):
        return self.__graph.neighbors(full_name)

    def find_packets_by_status(self, status):
        self.__logger.debug("Find packets by status = %s...", status)
        list = []
        for each in self.__graph.nodes():
            packet = self.__graph.get_node_attributes(each)
            if status == packet.status:
                list.append(packet)
        self.__logger.debug("[%s] packets has been found", len(list))

        return list

    def get_all(self):
        self.__logger.debug("Get all packets")

        return [self.__graph.node_attr[node] for node in self.__graph.nodes()]

    def get_status(self, full_name):
        self.__logger.debug("Get status of packet with with full_name  = %s...", full_name)
        packet = self.find_packet_by_fullname(full_name)
        self.__logger.debug("Status of packet %s is %s", full_name, packet.status)

        return packet.status

    def update(self, packet):
        is_packet_installed = self.find_packet_by_fullname(packet.name)
        if not is_packet_installed:
            self.add(packet)
        else:
            self.__storage.update(packet)
            self.__graph.node_attr[packet.name] = packet

            # TODO: NOT GOOD IMPLEMENTATION

    def update_status(self, full_name, status):
        self.__logger.debug("Update packet with name  = %s...", full_name)
        packet = self.find_packet_by_fullname(full_name)
        packet.status = status
        self.__storage.update(packet)

        self.__logger.debug("Update graph...")
        self.__graph.node_attr[full_name] = packet
        self.__logger.debug("Update packet with name  = %s and status = %s was successful", full_name, status)

    def add(self, packet):
        packet_fullname = packet.name
        self.__logger.debug("Add packet with name  = %s", packet_fullname)
        if self.find_packet_by_fullname(packet_fullname):
            message = "Packet %s is already in the repository" % packet_fullname
            self.__logger.error(message)
            raise RepositoryError(message)

        entry_packet = self.__storage.add(packet)
        self.__add_packet_to_graph(entry_packet)

    def remove(self, full_name):
        self.__logger.debug("Remove packet by full_name = %s", full_name)
        self.__storage.remove(full_name)

        self.__graph.del_node(full_name)
        self.__update_edges()
        self.__logger.debug("Remove packet by full_name = %s was successful", full_name)

    def find_child_packets(self, full_name):
        parents = [edge[0] for edge in self.__graph.edges() if edge[1] == full_name]

        return [self.find_packet_by_fullname(packet_name) for packet_name in parents]

    def __add_node(self, packet):
        self.__graph.add_node(packet.name, packet)

    def __add_packet_to_graph(self, packet):
        self.__logger.debug("Update graph")
        self.__add_node(packet)
        self.__update_edges()
        self.__logger.debug("Add packet with name  = %s was successful", packet.name)

    def __update_vertices(self):
        list = self.__storage.entries

        for each in list:
            node_name = each.name
            if node_name not in self.__graph.nodes():
                self.__add_node(each)

    def __update_edges(self):
        for each in self.__graph.nodes():
            packet = self.__graph.get_node_attributes(each)
            for node in self.__graph.nodes():
                node_packet = self.__graph.get_node_attributes(node)
                if packet.meta.is_in_depends(node_packet.meta.name, node_packet.meta.version):
                    self.__graph.add_edge(each, node)

    def __fill_graph(self):
        self.__graph = graph.digraph()

        self.__update_vertices()
        self.__update_edges()

class ScriptEntry:
    def __init__(self, name, content):
        self.__name = name
        self.__content = content

    @property
    def name(self):
        return self.__name

    @property
    def content(self):
        return self.__content
